import pyglet
import math
from random import random
from pyglet_utils import *

class CollisionSprite(pyglet.sprite.Sprite):
    def left_edge(self):
#        if isinstance(self.image, pyglet.image.AbstractImage):
#            return self.image.x - self.image.anchor_x
#        else:
        return self.x

    def right_edge(self):
        return self.left_edge() + self.width

    def bottom_edge(self):
#        if isinstance(self.image, pyglet.image.AbstractImage):
#            return self.image.y - self.image.anchor_y
#        else:
        return self.y

    def top_edge(self):
        return self.bottom_edge() + self.height

    def intersects(self, sprite):
        if not (sprite.left_edge() < self.left_edge() or
            sprite.right_edge() > self.right_edge() or
            sprite.top_edge() > self.top_edge() or
            sprite.bottom_edge() < self.bottom_edge()):
            return self, sprite

    def intersect_point(self, x, y):
        if (x > self.left_edge() and x < self.right_edge()
            and y > self.bottom_edge() and y < self.top_edge()):
            return True


class GameScreen(object):
    """
    The class of Screen Displayed when a game is actually in progress
    """
    bullets = pyglet.graphics.Batch()
    batch = pyglet.graphics.Batch()
    _sprites = []
    _bullets = []
    _copters = []
    def __init__(self, window):
        width, height = window.width, window.height
        self.width = width
        self.height = height
        self.window = window
        self.fps = 60.0
        self.score = 0

        self.setup_text()

        bgimg = load_image('background.png')
        self.bg = pyglet.sprite.Sprite(bgimg, 0, 0)

        self.barrel = Barrel(self)

        baseimg = load_image('base.png')
        baseimg.anchor_x = baseimg.width / 2.0
        self.base = pyglet.sprite.Sprite(baseimg, self.width / 2.0, 0,
                                         batch=self.batch)

    def run(self, callback):
        self.end_game = callback
        pyglet.clock.schedule(self.barrel.update, 1 / self.fps)
        self.window.push_handlers(self.on_key_press)
        pyglet.clock.schedule(self.update, 1 / self.fps)

    def setup_text(self):
        style = \
        dict(font_name='Arial', bold=True, color=(255, 255, 255, 255), font_size=24)
        score_text = pyglet.text.decode_text("Score")
        lives_text = pyglet.text.decode_text("Lives")
        score_num = pyglet.text.decode_text("2340")
        lives_num = pyglet.text.decode_text("0")
        score_num.set_style(0, 10, style)
        lives_num.set_style(0, 2, style)
        self.score_num = \
        pyglet.text.DocumentLabel(score_num, 10, 10, batch=self.batch)
        self.lives_num = \
        pyglet.text.DocumentLabel(lives_num, self.width - 10, 10,
                                  anchor_x='right', batch=self.batch)
        style['font_size'] = 10
        score_text.set_style(0, 6, style)
        lives_text.set_style(0, 6, style)
        t = pyglet.text.layout.TextLayout(score_text, batch=self.batch)
        t.x = 10
        t.y = 40
        lt = pyglet.text.layout.TextLayout(lives_text, batch=self.batch)
        lt.anchor_x = 'right'
        lt.x = self.width - 10
        lt.y = 40

    def add_copter(self, copter):
        self._sprites.append(copter)
        self._copters.append(copter)
        pyglet.clock.schedule(copter.update, 1 / self.fps)

    def remove_copter(self, copter):
        pyglet.clock.unschedule(copter.update)
        copter.image.frames.append(copter.last_frame)
        self._copters.remove(copter)
        self._sprites.remove(copter)

    def add_bullet(self, bullet):
        self._sprites.append(bullet)
        self._bullets.append(bullet)
        pyglet.clock.schedule(bullet.update, 1 / self.fps)

    def remove_bullet(self, bullet):
        try:
            pyglet.clock.unschedule(bullet.update)
            self._bullets.remove(bullet)
            self._sprites.remove(bullet)
        except ValueError:
            pass

    def update(self, dt, expected):
#        if random() < 1/(self.fps * 4): #a copter every 4 seconds, with 60fps
#            self.add_copter(Copter(self))

        if len(self._copters) != 1:
            self.add_copter(Copter(self))

        cs = [c.intersects(b) for b in self._bullets for c in self._copters]

        for t in cs:
            if t != None:
                self.remove_bullet(t[1])
                self.remove_copter(t[0])
                self.score_num.text = str(int(self.score_num.text) + 10)

        if int(self.lives_num.text) == 0:
            self.end_game(int(self.score_num.text))

    def render(self):
        self.bg.draw()
        self.bullets.draw()
        self.batch.draw()

    def on_key_press(self, key, modifiers):
        if key == pyglet.window.key.SPACE:
            x = (self.barrel.x +
                 (math.sin(math.radians(self.barrel.rotation)) * 50))
            y = self.barrel.y + math.cos(math.radians(self.barrel.rotation)) * 50
            self.add_bullet(Bullet(self, x, y, self.barrel.rotation))


    def __del__(self):
        pyglet.clock.unschedule(self.barrel.update)


class Copter(CollisionSprite):
    """
    A sprite representing a helicopter, one of two enemies
    """
    def __init__(self, screen):
        self.screen = screen

        img = load_image('copter.png')
        imgs = pyglet.image.ImageGrid(img, 4, 1)

        self.last_frame = pyglet.image.AnimationFrame(imgs[0], None)
        #imgs.anchor_y = imgs.height #Sprite is anchored by top right corner
        anim = pyglet.image.Animation.from_image_sequence(imgs, 0.01)
        super(Copter, self).__init__(anim, -imgs[0].width,
                                      screen.height-imgs[0].height-5,
                                      batch = screen.batch)


    def off_screen(self):
        if self.x > self.screen.width:
            return True
        else:
            return False

    def update(self, dt, expected):
        """
        Called before the drawing of the sprite to calculate its new
        position.

        @type  dt: number
        @param dt: The time, in seconds, since last update
        """
        self.x += int(256 * dt)
        if self.off_screen():
            self.screen.lives_num.text = str(int(self.screen.lives_num.text) - 1)
            self.screen.remove_copter(self)


class Barrel(pyglet.sprite.Sprite):
    direction = 0
    def __init__(self, screen):
        self.screen = screen

        img = load_image('barrel.png')
        img.anchor_x = img.width / 2.0

        self.screen.window.push_handlers(self.on_key_press)
        self.screen.window.push_handlers(self.on_key_release)

        super(Barrel, self).__init__(img, screen.width / 2.0, 36,
                                       batch=screen.batch)

    def on_key_press(self, symbol, modifiers):
        if symbol == pyglet.window.key.LEFT:
            self.direction = -1
        elif symbol == pyglet.window.key.RIGHT:
            self.direction = 1

    def on_key_release(self, symbol, modifiers):
        if ((self.direction == -1 and symbol == pyglet.window.key.LEFT) or
           (self.direction == 1 and symbol == pyglet.window.key.RIGHT)):
            self.direction = 0

    def update(self, dt, expected):
        self.rotation += self.direction * 100 * dt

        if not (self.direction != 0
                and self.rotation <= 89
                and self.rotation >= -89):
            self.direction = 0


class Bullet(CollisionSprite):
    def __init__(self, screen, x, y, heading):
        self.screen = screen

        img = load_image('bullet.png')
        img.anchor_x = img.width / 2.0
        img.anchor_y = img.height / 2.0
        super(Bullet, self).__init__(img, x, y, batch=screen.bullets)

        heading = heading
        speed = 200

        self.dx = math.sin(math.radians(heading)) * speed
        self.dy = math.cos(math.radians(heading)) * speed

    def update(self, dt, expected):
        self.x += self.dx * dt
        self.y += self.dy * dt
        if self.off_screen(): self.screen.remove_bullet(self)

    def off_screen(self):
        margins = (0 - self.width, self.screen.width + self.width,
                   0 - self.height, self.screen.height + self.height)

        if (self.x < margins[0] or self.x > margins[1]
            or self.y < margins[2] or self.y > margins[3]):
            self.screen.remove_bullet(self)

